package common

import (
	"encoding/xml"
	"fmt"
	"github.com/sassoftware/go-rpmutils"
	"sort"
	"unsafe"
)

type Config struct {
	Yum YumConf `mapstructure:"yum"`
}

type YumConf struct {
	StoreDir  string          `mapstructure:"store"`
	ThreadNum int             `mapstructure:"thread_num"`
	YumRepos  YumRepo         `mapstructure:"yum_repos"`
	Attachs   []YumRepoAttach `mapstructure:"attach"`
	Repos     []YumRepoItem   `mapstructure:"repos"`
	Proxy     string          `mapstructure:"proxy"`
}

type YumRepo struct {
	Dirs  []string      `mapstructure:"dirs"`
	Hosts []YumRepoHost `mapstructure:"hosts"`
}

type YumRepoHost struct {
	Name      string   `mapstructure:"name"`
	Host      string   `mapstructure:"host"`
	RemoteDir string   `mapstructure:"remote_dir"`
	Include   []string `mapstructure:"include"`
	Exclude   []string `mapstructure:"exclude"`
}

type YumRepoAttach struct {
	Name    string `mapstructure:"name"`
	BaseUrl string `mapstructure:"baseurl"`
	Enabled bool   `mapstructure:"enabled"`
}

type YumRepoItem struct {
	Name       string   `mapstructure:"name"`
	Version    string   `mapstructure:"version"`
	Releasever string   `mapstructure:"el"`
	Url        string   `mapstructure:"url"`
	Dir        string   `mapstructure:"dir"`
	Packages   []string `mapstructure:"packages"`
	Enabled    bool     `mapstructure:"enabled"`
	UseProxy   bool     `mapstructure:"use_proxy"`
}

type Data struct {
	Type         string   `xml:"type,attr"`
	Location     Location `xml:"location"`
	Checksum     Checksum `xml:"checksum"`
	OpenChecksum Checksum `xml:"open-checksum"`
	Timestamp    float64  `xml:"timestamp"`
	Size         uint64   `xml:"size"`
}

type Repomd struct {
	XMLName  xml.Name `xml:"repomd"`
	Revision string   `xml:"revision"`
	Data     []Data   `xml:"data"`
}

type Location struct {
	Href string `xml:"href,attr"`
}

type Checksum struct {
	Type  string `xml:"type,attr"`
	PkgId string `xml:"pkgid,attr,omitempty"`
	Value string `xml:",chardata"`
}

type Metadata struct {
	XMLName      xml.Name  `xml:"metadata"`
	PackageCount uint64    `xml:"packages,attr"`
	Packages     []Package `xml:"package"`
}

type Version struct {
	Epoch   uint64 `xml:"epoch,attr,omitempty"`
	Version string `xml:"ver,attr,omitempty"`
	Release string `xml:"rel,attr,omitempty"`
}

func (v Version) String() string {
	if v.Epoch > 0 {
		return fmt.Sprintf("%d:%s-%s", v.Epoch, v.Version, v.Release)
	} else {
		return fmt.Sprintf("%s-%s", v.Version, v.Release)
	}
}

type Time struct {
	File  uint64 `xml:"file,attr"`
	Build uint64 `xml:"build,attr"`
}

type Size struct {
	Package   uint64 `xml:"package,attr"`
	Installed uint64 `xml:"installed,attr"`
	Archive   uint64 `xml:"archive,attr"`
}

type HeaderRange struct {
	Start uint64 `xml:"start,attr"`
	End   uint64 `xml:"end,attr"`
}

type Entry struct {
	XMLName xml.Name `xml:"http://linux.duke.edu/metadata/rpm entry"`
	Version
	Name  string `xml:"name,attr"`
	Flags string `xml:"flags,attr,omitempty"`
}

type Format struct {
	XMLName     xml.Name    `xml:"format"`
	License     string      `xml:"http://linux.duke.edu/metadata/rpm license"`
	Vendor      string      `xml:"http:/linux.duke.edu/metadata/rpm vendor"`
	Group       string      `xml:"http://linux.duke.edu/metadata/rpm group"`
	BuildHost   string      `xml:"http://linux.duke.edu/metadata/rpm buildhost"`
	SourceRPM   string      `xml:"http://linux.duke.edu/metadata/rpm sourcerpm"`
	HeaderRange HeaderRange `xml:"http://linux.duke.edu/metadata/rpm header-range"`
	Provides    []Entry     `xml:"http://linux.duke.edu/metadata/rpm provides>entry"`
	Requires    []Entry     `xml:"http://linux.duke.edu/metadata/rpm requires>entry"`
	Files       []string    `xml:"file"`
}

type Package struct {
	XMLName     xml.Name `xml:"package"`
	Type        string   `xml:"type"`
	Name        string   `xml:"name"`
	Arch        string   `xml:"arch"`
	Version     Version  `xml:"version"`
	Checksum    Checksum `xml:"checksum"`
	Summary     string   `xml:"summary"`
	Description string   `xml:"description"`
	Packager    string   `xml:"packager"`
	Url         string   `xml:"url"`
	Time        Time     `xml:"time"`
	Size        Size     `xml:"size"`
	Location    Location `xml:"location"`
	Format      Format   `xml:"format"`
}

func (s *Package) size() int {
	size := int(unsafe.Sizeof(*s))
	size += len(s.Type)
	size += len(s.Name)
	size += len(s.Arch)
	size += len(s.Summary)
	size += len(s.Description)
	size += len(s.Packager)
	size += len(s.Url)
	return size
}

type DownItem struct {
	ProxyUrl string
	StoreDir string
	RepoUrl  string
	Package  Package
	RepoItem YumRepoItem
}

type Packages []Package

func (p Packages) Len() int {
	return len(p)
}

func (p Packages) Less(i, j int) bool {
	return rpmutils.Vercmp(fmt.Sprintf("%s-%s", p[i].Name, p[i].Version.String()), fmt.Sprintf("%s-%s", p[j].Name, p[j].Version.String())) == 1
}

func (p Packages) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func (p *Packages) Sort() {
	sort.Sort(p)
}
